#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
#include <string>

#define MAXSZOFPL 128
#define MAXSZOFCL 128
#define MAXOFARGC 128
#define MAXOFENVC 128
#define FORMAT "[%s@%s %s]# "

int argc;
char* argv[MAXOFARGC];//命令行参数表

extern char** environ;
int envc;
char* env[MAXOFENVC];//环境变量表

char buf[128];
char cwdbuf[128];

int lastExitCode = 0;//最新退出码

void initTheEnvList()
{
    //模拟父shell去配置文件获取环境变量，并将其放入环境变量表中
    int i;
    envc = 0;
    for(i = 0; environ[i]; ++i)
    {
        env[envc] = (char*)malloc(strlen(environ[i]) + 1);
        strcpy(env[envc], environ[i]);
        envc++;
    }
    env[envc] = NULL;
    //环境变量表导入上下文
    for(i = 0; i < envc; ++i)
    {
        putenv(env[i]);
    }
    environ = env;
}

const char* getUSER()
{
    const char* user = getenv("USER");
    return user == NULL ? "none" : user;
}

const char* getHOSTNAME()
{
    const char* hostName = getenv("HOSTNAME");
    return hostName == NULL ? "none" : hostName;
}

const char* getPWD()
{
    //const char* pwd = getenv("PWD");
    const char* pwd = getcwd(buf, sizeof(buf));
    if(pwd != NULL)
    {
        snprintf(cwdbuf, sizeof(cwdbuf), "PWD=%s", buf);
        putenv(cwdbuf);
    }

    return pwd == NULL ? "none" : pwd;
}

std::string getDIR(const char* pwd)
{
    std::string s = pwd;
    if(s == "/")
        return "/";
    size_t it = s.rfind("/");
    if(it == std::string::npos)
        return "BUG";
    return s.substr(it + 1); 
}

void createPromptLines(char pL[], int sz)
{
   //snprintf(pL, sz, FORMAT, getUSER(), getHOSTNAME(), getPWD()); 
   snprintf(pL, sz, FORMAT, getUSER(), getHOSTNAME(), getDIR(getPWD()).c_str()); 
}

void printPrompLines()
{
    char prompLines[MAXSZOFPL] = {'\0'};
    createPromptLines(prompLines, sizeof(prompLines));
    printf("%s", prompLines);
    fflush(stdout);
}

bool obtainTheCommandLine(char* commanLine, int sz)
{
   char* ret = fgets(commanLine, sz, stdin);
   if(ret == NULL)
       return false;
   commanLine[strlen(ret) - 1] = 0;
   if(strlen(ret) == 0)
       return false;
   return true;
}

bool splitCommandLine(char* cL)
{
#define SEP " "
    argc = 0;
    argv[argc++] = strtok(cL, SEP);
    while((bool)(argv[argc++] = strtok(NULL, SEP)));
    argv[argc] = NULL;
    argc--;
    return argc > 0 ? true : false;
}
void Cd()
{

    if(argc == 1)
    {
        //cd
        chdir(getenv("HOME"));
    }
    else 
    {
        std::string dest = argv[1];
        if(dest == "-")
        {
                
        }
        else if(dest == "~")
        {
            chdir(getenv("HOME"));
        }
        else if(dest == ".")
        {

        }
        else if(dest == "..")
        {
                
        }
        else 
        {
            chdir(dest.c_str());
        }
    
    }
}

void Echo()
{
    
    if(argc == 2)
    {
        std::string op = argv[1];
        if(op[0] == '$')
        {
            if(op[1] == '?')
            {
                printf("%d\n", lastExitCode);
                lastExitCode = 0;
            }
            else 
            {
                std::string key = op.substr(1);
                const char* val = getenv(key.c_str());
                printf("%s\n", val);
            }
        }
        else 
        {
            printf("%s\n", op.c_str());
        }
    }
}

bool handleBuiltInCommands()
{
    std::string Com = argv[0];
    if(Com == "cd")
    {
        Cd();
        return true;
    }
    if(Com == "echo")
    {
        Echo();
        return true;
    }
    return false;
}
int executiveCommand()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child process
        execvp(argv[0], argv);
        return errno;
    }
    int status;
    waitpid(-1, &status, 0);
    lastExitCode = (status >> 8) & 0xFF;
    return errno;
}

void dele()
{
    int i = 0; 
    for(i = 0; i < envc; ++i)
    {
        free(env[i]);
        env[i] = NULL;
    }
}

int main()
{
    //模拟shell启动时，去配置文件获取环境变量
    initTheEnvList();

    while(1)
    {

        //打印提示条
        printPrompLines();

        //获取命令行
        char commanLine[MAXSZOFCL];
        if(!obtainTheCommandLine(commanLine, sizeof(commanLine)))
            continue;

        //分割命令行，并且存入命令行参数表
        if(!splitCommandLine(commanLine))
            continue;
        
        //判断并且处理内建命令
        if(handleBuiltInCommands())
            continue;

        //创建子进程，执行命令
        executiveCommand();
    
    }
    dele();
    return 0;
}





